package com.okaixz.client.service.impl;

import com.alibaba.dashscope.app.*;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.fastjson2.JSON;
import com.google.gson.JsonObject;
import com.okaixz.client.controller.WebSocketServer;
import com.okaixz.client.model.domain.KbAssistant;
import com.okaixz.client.model.domain.KbCustomerAssistant;
import com.okaixz.client.model.domain.KbUserAssistantInfo;
import com.okaixz.client.model.dto.*;
import com.okaixz.client.service.*;
import com.okaixz.common.client.WsResult;
import com.okaixz.common.client.exception.CommonErrorCode;
import com.okaixz.common.constant.CommonConstants;
import com.okaixz.common.constant.WsRequestCode;
import com.okaixz.common.constant.WsResultCode;
import com.okaixz.manager.app.domain.App;
import com.okaixz.manager.app.service.IAppService;
import io.reactivex.Flowable;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.util.List;

/**
 * <p>
 * 写作服务实现类
 * </p>
 *
 * @author cdz
 * @since 2024-10-08
 */
@Service
@Slf4j
public class XieZuoServiceImpl implements IXieZuoService {

    @Autowired
    private IAppService appService;
    @Autowired
    private IKbAssistantService kbAssistantService;
    @Autowired
    private IKbCustomerAssistantService customerAssistantService;
    @Autowired
    private IKbUserAssistantInfoService userAssistantInfoService;
    @Autowired
    private ITokenService tokenService;
    @Autowired
    private IUserService userService;
    @Autowired
    private IHuashuService huashuService;


    @Override
    public void xieZuo(String sessionId, WsRequest request, UserDto userDto) {
        Long appId = request.getAppId();
        String input = request.getInput();

        if (WebSocketServer.checkToken) {
            //校验权限
            if (!checkVip(userDto)) {
                WebSocketServer.sendData(sessionId, WsResult.error(CommonErrorCode.CHECK_VIP_FAIL));
                WebSocketServer.closeSession(sessionId);
                return;
            }
        }
        if (appId == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }
        App app = appService.selectAppById(appId);
        if (app == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }
        Long type = app.getType();
        if (type == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }

        if (type == 2) {
            //千问写作
            try {
                streamCall(sessionId, request, userDto, app);
            } catch (Exception e) {
                e.printStackTrace();
                WebSocketServer.closeSession(sessionId);
            }
            return;
        }
        //讯飞写作
        String xfAssistantsId = app.getXfAssistantsId();
        if (xfAssistantsId == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }
        String xfAppId = app.getXfAppId();
        if (xfAppId == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }

        //更新应用写作次数
        app.setUseCount(app.getUseCount() + 1);
        appService.updateApp(app);


        try {
            //            URI uri = new URI("ws://spark-openapi.cn-huabei-1.xf-yun.com/v1/assistants/adfacmf1zmit_v1");
            URI uri = new URI("ws://spark-openapi.cn-huabei-1.xf-yun.com/v1/assistants/" + xfAssistantsId);
            WebSocketClient client = new WebSocketClient(uri) {
                private int takeTokens;//消耗的字数

                @Override
                public void onOpen(ServerHandshake serverHandshake) {
                    System.out.println("新连接已打开");
                    AssistantRequest assistantRequest = new AssistantRequest(xfAppId, "uid", input == null ? "" : input);
                    String jsonStr = JSON.toJSONString(assistantRequest);
                    System.out.println("request:" + jsonStr);
                    try {
                        send(jsonStr);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }


                @Override
                public void onMessage(String s) {
                    try {
                        System.out.println("接收到消息: " + s);
                        AssistantResult assistantResult = JSON.parseObject(s, AssistantResult.class);
                        AssistantResult.HeaderDTO header = assistantResult.getHeader();
                        AssistantResult.PayloadDTO payload = assistantResult.getPayload();
                        if (payload == null) {
                            return;
                        }
                        List<AssistantResult.PayloadDTO.ChoicesDTO.TextDTO> text = payload.getChoices().getText();
                        if (text != null && text.size() != 0) {
                            String content = text.get(0).getContent();
                            WebSocketServer.sendData(sessionId, WsResult.success(content, WsRequestCode.getResultCode(WsRequestCode.XIE_ZUO)));
                            int length = content.length();
                            takeTokens += length;
                            log.info("消耗字数：{}", takeTokens);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onClose(int i, String s, boolean b) {
                    System.out.println("连接已关闭");
                    try {
                        //                        UserDto user = tokenService.refreshUserAndToken(userDto.getUserId());//更新token
                        if (userDto != null) {
                            Integer vipFlag = userDto.getVipFlag();
                            if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_VIP) {
                                userService.reduceTokens(userDto, takeTokens);
                            }
                            userDto.setToken(null);
                        }
                        WebSocketServer.sendData(sessionId, WsResult.success(userDto, WsResultCode.REFRESH_USER));
                        WebSocketServer.closeSession(sessionId);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onError(Exception e) {
                    e.printStackTrace();
                    try {
                        WebSocketServer.closeSession(sessionId);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            };
            client.connect();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void kbXieZuo(String sessionId, WsRequest request, UserDto userDto) {
        if (request.getAppId() == null||request.getInput() == null) {
            WebSocketServer.sendData(sessionId, WsResult.error(CommonErrorCode.PARAM_ERROR));
            return;
        }
        long appId = request.getAppId();
        String input = request.getInput();

        if (WebSocketServer.checkToken) {
            //校验权限
            if (!checkVip(userDto)) {
                WebSocketServer.sendData(sessionId, WsResult.error(CommonErrorCode.CHECK_VIP_FAIL));
//                WebSocketServer.closeSession(sessionId);
                return;
            }
        }


        KbAssistant userAssistantInfo=null;
        if(appId>CommonConstants.SYSTEM_ASSISTANT_OFFSET){
            //自定义标签
            appId=appId-CommonConstants.SYSTEM_ASSISTANT_OFFSET;
            KbCustomerAssistant customerAssistant = customerAssistantService.getById(appId);
            if(customerAssistant!=null){
                userAssistantInfo=customerAssistant.transfornToKbAssistant();
            }
        }else {
            //系统标签
            userAssistantInfo = kbAssistantService.getById(appId);
            if (userAssistantInfo == null) {
                //兼容老版本 自定义标签
                KbUserAssistantInfo assistantInfo = userAssistantInfoService.getById(appId);
                if (assistantInfo != null) {
                    userAssistantInfo = new KbAssistant();
                    userAssistantInfo.setOutStyle(assistantInfo.getAssistantStyls());
                }
            }
        }


        if (userAssistantInfo == null) {
            WebSocketServer.sendData(sessionId, WsResult.error(CommonErrorCode.ASSISTANT_NOT_EXIST));
            return;
        }


        //千问写作
        try {
            if(request.getInputType()==0){
                //只有帮回复有话术库
                String huaShu = huashuService.getHuaShu(request.getInput(), userAssistantInfo.getOutStyle(), userDto.getSex());
                if (huaShu != null) {
                    //创作结束
                    int tokens = huaShu.length();
                    Integer vipFlag = userDto.getVipFlag();

                    if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_VIP) {
                        userService.reduceTokens(userDto, tokens);
                    }else if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TIMES_VIP) {
                        userService.reduceRemainTimes(userDto);
                    }
                    userDto.setToken(null);
                    UserDto user = tokenService.refreshUserAndToken(userDto.getUserId());
                    huaShu=huaShu+"(AI生成)";
                    WebSocketServer.sendData(sessionId, WsResult.success(huaShu, WsRequestCode.getResultCode(WsRequestCode.XIE_ZUO)));
                    WebSocketServer.sendData(sessionId, WsResult.success(user, WsResultCode.REFRESH_USER));
                    return;
                }
            }
            streamCall(sessionId, request, userDto, userAssistantInfo);
        } catch (Exception e) {
            e.printStackTrace();
            WebSocketServer.sendData(sessionId, WsResult.error(CommonErrorCode.UNKOWN));
        }

    }

    private void streamCall(String sessionId, WsRequest request, UserDto userDto, KbAssistant assistant) throws NoApiKeyException, InputRequiredException {
        //千问写作
        String qwenAppid = "";
//        if (qwenAppid == null) {
//            WebSocketServer.closeSession(sessionId);
//            return;
//        }
        TakeTokensBean bean = new TakeTokensBean();//消耗的字数

        JsonObject jsonObject=new JsonObject();
        String outStyle = assistant.getOutStyle();
        jsonObject.addProperty("style", outStyle);


        StringBuilder sb = new StringBuilder();
        switch (request.getInputType()) {
            case 0:
//                qwenAppid="ca11e7bcf2e94ee4b5fd1ba933659c1d";
                qwenAppid="a6c06ea0bbfe40f6b2b41bb078daa3f0";
                //        对方的消息：我想你了，要求输出风格：古文诗、词，只要输出一句话
                //待回消息“我不想去看电影”，希望表现出的特质霸道总裁
//                sb.append("待回消息:");
//                sb.append(request.getInput());
//                sb.append("\n##限制：\n");
//                sb.append("- 希望表现出的特质或风格:");
//                sb.append(assistant.getAssistantStyls());
//                sb.append("\n- 只要输出必须要完整！");

//                sb.append("你是聊天高手，当对方说：${input}时，我想表现出${style}，我该怎么回？要求：直接帮我生成回复内容，要求只生成回复内容不要其它多余信息");
                sb.append("聊天过程中对方说:\"");
                sb.append(request.getInput());
                sb.append("\"，我想表现出:");
                sb.append(outStyle);
                sb.append("。该怎么回复？要求：只生成回复内容不要其它多余信息！只需要输入一两句简短而精辟的回复！！！！");

                break;
            case 1:
                //        原文：我想你了，要求输出风格：古文诗词，只要输出一句话
//                qwenAppid="6a9421ab232b49b298150725c53f10f6";
                qwenAppid="d6324db20dee4c4292b967b5ded4920c";
                sb.append("你是一个文本润色专家，帮我把\"");
                sb.append(request.getInput());
//                sb.append("\n##限制：\n");
                //        sb.append("- 字数严格控制在");
                //        sb.append(request.getZiShu());
                //        sb.append("字左右");
                sb.append("\"这句话改写成");
                sb.append(outStyle);
                sb.append("风格的,只要输出简短的一句话！！！要润色！！！");

                break;
            case 2:
                break;
        }



        String prompt = sb.toString();
        log.info("prompt:{}", prompt);
        ApplicationParam param = ApplicationParam.builder()
                // 若没有配置环境变量，可用百炼API Key将下行替换为：api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中，以减少API Key泄露风险。
                //                .apiKey(System.getenv("apikey"))
//                .apiKey("sk-d9e586f601b34a37868eaccc81268ee6")
                .apiKey("sk-4a074d6ef80d4280b73fd318cf81a5d7")
                .appId(qwenAppid)
                .prompt(prompt)
//                .bizParams(jsonObject)
                .incrementalOutput(true)
                .build();


        Application application = new Application();
        Flowable<ApplicationResult> result = application.streamCall(param);
        result.blockingForEach(data -> {
            ApplicationOutput output = data.getOutput();
            String finishReason = output.getFinishReason();
//            log.info("收到回复：{}", output.getText());
//            log.info("finishReason：{}", finishReason+"");
            int tokens = bean.getTokens();


            String text = output.getText();
            if (text == null) {
                return;
            }
            int length = text.length();
            tokens += length;
            bean.setTokens(tokens);
            log.info("消耗字数：{}", tokens);

            // 使用 flexmark-java 将 Markdown 转换为 HTML
            //            MutableDataSet options = new MutableDataSet();
            //            Parser parser = Parser.builder(options).build();
            //            HtmlRenderer renderer = HtmlRenderer.builder(options).build();
            //
            //            // You can re-use parser and renderer instances
            //            Node document = parser.parse(text);
            //            text = renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"





            if (finishReason != null && finishReason.equals("stop")) {
                //创作结束
                Integer vipFlag = userDto.getVipFlag();
                String qWSessionId = output.getSessionId();
                if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_VIP) {
                    userService.reduceTokens(userDto, tokens);
                }else if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TIMES_VIP) {
                    userService.reduceRemainTimes(userDto);
                }
                userDto.setToken(null);
                UserDto user = tokenService.refreshUserAndToken(userDto.getUserId());
                text=text+"(AI生成)";
                WebSocketServer.sendData(sessionId, WsResult.success(text, WsRequestCode.getResultCode(WsRequestCode.XIE_ZUO)));
                WebSocketServer.sendData(sessionId, WsResult.success(user, WsResultCode.REFRESH_USER));
            }else {
                WebSocketServer.sendData(sessionId, WsResult.success(text, WsRequestCode.getResultCode(WsRequestCode.XIE_ZUO)));
            }


            //            System.out.printf("requestId: %s, text: %s, finishReason: %s\n",
            //                    data.getRequestId(), text, finishReason);//结束时 finishReason: stop
            //            System.out.printf("sessionId: %s\n", sessionId);
            ApplicationUsage usage = data.getUsage();
            List<ApplicationUsage.ModelUsage> models = usage.getModels();
            for (ApplicationUsage.ModelUsage model : models) {
                System.out.printf("modelid: %s, InputTokens: %d, getOutputTokens: %d\n",
                        model.getModelId(), model.getInputTokens(), model.getOutputTokens());
            }

        });
    }

    private void streamCall(String sessionId, WsRequest request, UserDto userDto, App app) throws NoApiKeyException, InputRequiredException {
        //千问写作
        String qwenAppid = app.getQwenAppid();
        if (qwenAppid == null) {
            WebSocketServer.closeSession(sessionId);
            return;
        }
        TakeTokensBean bean = new TakeTokensBean();//消耗的字数
        StringBuilder sb = new StringBuilder();
        //        原文：我想你了，要求输出风格：古文诗词，只要输出一句话
        sb.append("原文:");
        sb.append(request.getInput());
        sb.append("\n##限制：\n");
        sb.append("- 字数严格控制在");
        sb.append(request.getZiShu());
        sb.append("字左右");

        String prompt = sb.toString();
        log.info("prompt:{}", prompt);
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("tokens", request.getZiShu());
        ApplicationParam param = ApplicationParam.builder()
                // 若没有配置环境变量，可用百炼API Key将下行替换为：api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中，以减少API Key泄露风险。
                //                .apiKey(System.getenv("aikeyboard"))
                .apiKey("6666")
                .appId(qwenAppid)
                .prompt(prompt)
                .incrementalOutput(true)
                .bizParams(jsonObject)
                .build();

        Application application = new Application();
        Flowable<ApplicationResult> result = application.streamCall(param);
        result.blockingForEach(data -> {
            ApplicationOutput output = data.getOutput();
            String finishReason = output.getFinishReason();
            int tokens = bean.getTokens();
            if (finishReason != null && finishReason.equals("stop")) {
                //创作结束
                Integer vipFlag = userDto.getVipFlag();
                String qWSessionId = output.getSessionId();
                if (vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_VIP) {
                    userService.reduceTokens(userDto, tokens);
                }
                userDto.setToken(null);
                WebSocketServer.sendData(sessionId, WsResult.success(userDto, WsResultCode.REFRESH_USER));
                WebSocketServer.closeSession(sessionId);
                return;
            }

            String text = output.getText();
            if (text == null) {
                return;
            }
            int length = text.length();
            tokens += length;
            bean.setTokens(tokens);
            log.info("消耗字数：{}", tokens);

            // 使用 flexmark-java 将 Markdown 转换为 HTML
            //            MutableDataSet options = new MutableDataSet();
            //            Parser parser = Parser.builder(options).build();
            //            HtmlRenderer renderer = HtmlRenderer.builder(options).build();
            //
            //            // You can re-use parser and renderer instances
            //            Node document = parser.parse(text);
            //            text = renderer.render(document);  // "<p>This is <em>Sparta</em></p>\n"


            WebSocketServer.sendData(sessionId, WsResult.success(text, WsRequestCode.getResultCode(WsRequestCode.XIE_ZUO)));

            //            System.out.printf("requestId: %s, text: %s, finishReason: %s\n",
            //                    data.getRequestId(), text, finishReason);//结束时 finishReason: stop
            //            System.out.printf("sessionId: %s\n", sessionId);
            ApplicationUsage usage = data.getUsage();
            List<ApplicationUsage.ModelUsage> models = usage.getModels();
            for (ApplicationUsage.ModelUsage model : models) {
                System.out.printf("modelid: %s, InputTokens: %d, getOutputTokens: %d\n",
                        model.getModelId(), model.getInputTokens(), model.getOutputTokens());
            }

        });
    }

    /**
     * 检查用户权限
     *
     * @param user
     * @return
     */
    private boolean checkVip(UserDto user) {
        boolean result = false;
        Integer vipFlag = user.getVipFlag();
        if (vipFlag == null || vipFlag == CommonConstants.VIP_FLAG_NOT_VIP || vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_NO_TOKENS) {
            result = false;
        } else {
            result = true;
        }
        System.out.println("checkVip result=" + result);
        return result;
    }
    /**
     * 检查用户权限
     *
     * @param user
     * @return
     */
    private boolean kbCheckVip(UserDto user) {
        boolean result = false;
        Integer vipFlag = user.getVipFlag();
        if (vipFlag == null || vipFlag == CommonConstants.VIP_FLAG_NOT_VIP || vipFlag == CommonConstants.VIP_FLAG_LIMIT_TOKENS_NO_TOKENS) {
            result = false;
        } else {
            result = true;
        }
        System.out.println("checkVip result=" + result);
        return result;
    }
}
